#ifndef CELLMATH_H
#define CELLMATH_H

#include "common.h"
#include <math.h>

#define pi 3.141592653589793238462643383279

//!  A Cell Math class.
/*!
  It is class of all mathematical computations. All functions are static. Call these functions in another class by using CellMath::fun().
*/

class CellMath
{
public:
    //! A constructor.
    CellMath();

    //! Sets the elements of mat1 equal to the elements of mat2
    /*!
          \param mat1 matrix that is being assigned
          \param mat2 matrix whose elements are being copied
     */
    static void setEqual(double mat1[4][4], const double mat2[4][4]);

    //! Prints the elements of a 4 by 4 matrix
    static void print4by4(const double mat[4][4]);

    //! A line intersected with a plane
    /*!
          \param a0 x coordinate value of the start point.
          \param b0 x coordinate value of the end point.
          \param a1 y coordinate value of the start point.
          \param b1 y coordinate value of the end point.
          \param a1 z coordinate value of the start point.
          \param b1 z coordinate value of the end point.
          \param a a value in the plane function ax+by+cz+d=0.
          \param b b value in the plane function ax+by+cz+d=0.
          \param c c value in the plane function ax+by+cz+d=0.
          \param d d value in the plane function ax+by+cz+d=0.
          \param ix return the x coordinate value of the intersection point.
          \param iy return the y coordinate value of the intersection point.
          \param iz return the z coordinate value of the intersection point.
          \return return true if the line has a intersection point with the plane, or return false.
    */
    static bool lineIntersection(const double a0, const double b0, const double a1, const double b1, const double a2, const double b2,
                          const double a, const double b, const double c, const double d, double &ix, double &iy, double &iz);

    //! A edge intersected with a plane
    /*!
          \param a0 x coordinate value of the start point.
          \param b0 x coordinate value of the end point.
          \param a1 y coordinate value of the start point.
          \param b1 y coordinate value of the end point.
          \param a1 z coordinate value of the start point.
          \param b1 z coordinate value of the end point.
          \param a a value in the plane function ax+by+cz+d=0.
          \param b b value in the plane function ax+by+cz+d=0.
          \param c c value in the plane function ax+by+cz+d=0.
          \param d d value in the plane function ax+by+cz+d=0.
          \param ix return the x coordinate value of the intersection point.
          \param iy return the y coordinate value of the intersection point.
          \param iz return the z coordinate value of the intersection point.
          \return return the length between intersection point with start position.
    */
    static double edgeIntersection(const double a0, const double b0, const double a1, const double b1, const double a2, const double b2,
                                   const double a, const double b, const double c, const double d, double &ix, double &iy, double &iz);

    //! A traingle intersected with a plane
    //! A edge intersected with a plane
    /*!
          \param a a value in the plane function ax+by+cz+d=0.
          \param b b value in the plane function ax+by+cz+d=0.
          \param c c value in the plane function ax+by+cz+d=0.
          \param d d value in the plane function ax+by+cz+d=0.
          \param vx0 x coordinate value of the point 0.
          \param vy0 y coordinate value of the point 0.
          \param vz0 z coordinate value of the point 0.
          \param vx1 x coordinate value of the point 1.
          \param vy1 y coordinate value of the point 1.
          \param vz1 z coordinate value of the point 1.
          \param vx2 x coordinate value of the point 2.
          \param vy2 y coordinate value of the point 2.
          \param vz2 z coordinate value of the point 2.
          \param rvx0 return the x coordinate value of point 0 of the intersection line.
          \param rvy0 return the y coordinate value of point 0 of the intersection line.
          \param rvz0 return the z coordinate value of point 0 of the intersection line.
          \param rvx1 return the x coordinate value of point 1 of the intersection line.
          \param rvy1 return the y coordinate value of point 1 of the intersection line.
          \param rvz1 return the z coordinate value of point 1 of the intersection line.
          \return return true if the triangle has a intersection line with the plane, or return false.
    */
    static bool triangleIntersection(const double a, const double b, const double c, const double d,
                                     const double vx0, const double vy0, const double vz0,
                                     const double vx1, const double vy1, const double vz1,
                                     const double vx2, const double vy2, const double vz2,
                                     double &rvx0, double &rvy0, double &rvz0,
                                     double &rvx1, double &rvy1, double &rvz1);

    //! A triangle intersected with a line
    /*!
          \param pos the start position of the line.
          \param orientation the orientation of the line.
          \param v0 the first point of triangle.
          \param v1 the second point of triangle.
          \param v2 the third point of triangle.
          \return return length between the intersection point and the start position of the line, and return -1.0 if there is no intersection.
    */
    static double lineIntersectionTriangle(const Vector3 pos, const Vector3 orientation, const Vector3 v0, const Vector3 v1, const Vector3 v2);

    //! Construct the rotate matrix of the axis x.
    /*!
          \param degree the rotate degree around the axis x.
          \param mat return the rotate matrix.
    */
    static void rotateMatX(const double degree, double mat[4][4]);

    //! Construct the rotate matrix of the axis y.
    /*!
          \param degree the rotate degree around the axis y.
          \param mat return the rotate matrix.
    */
    static void rotateMatY(const double degree, double mat[4][4]);

    //! Construct the rotate matrix of the axis z.
    /*!
          \param degree the rotate degree around the axis z.
          \param mat return the rotate matrix.
    */
    static void rotateMatZ(const double degree, double mat[4][4]);

    //! Construct the rotation matrix from an axis and angle
    /*!
         \param degree angle of rotation
         \param axisX x direction of rotation
         \param axisY y direction of rotation
         \param axisZ z direction of rotation
         \param mat return the rotation matrix
     */
    static void rotateMatFromAxis(const double degree, const double axisX, const double axisY, const double axisZ, double mat[4][4]);

    //! Construct the translate matrix.
    /*!
          \param x translate value along axis x.
          \param y translate value along axis y.
          \param z translate value along axis z.
          \param mat return the translate matrix.
    */
    static void translateMat(const double x, const double y, const double z, double mat[4][4]);

    //! Construct the scale matrix.
    /*!
          \param x scale value along axis x.
          \param y scale value along axis y.
          \param z scale value along axis z.
          \param mat return the scale matrix.
    */
    static void scaleMat(const double x, const double y, const double z, double mat[4][4]);

    //! Compute the result of a 4*4 matrix mutiplay a 4 vector.
    /*!
          \param mat 4*4 matrix.
          \param v 4 vector.
          \param result 4*4 matrix.
    */
    static void matMutiplyVector(const double mat[4][4], const double v[4], double result[4]);

    //! Compute the result of a 4*4 matrix mutiplay a 4*4 matrix.
    /*!
          \param mat1 4*4 matrix.
          \param mat2 4*4 matrix.
          \param result 4*4 matrix.
    */
    static void matMutiply(const double mat1[4][4], const double mat2[4][4], double result[4][4]);

    //! Compute the inverse matrix
    /*!
          \param mat 4*4 matrix.
          \param result 4*4 matrix.
    */
    static void InverseMat(const double mat[4][4], double result[4][4]);

    //! Transpote a matrix
    /*!
          \param mat 4*4 matrix.
          \param result 4*4 matrix.
    */
    static void TranspoteMat(const double mat[4][4], double result[4][4]);

    //! Compute the angel between two 2D vectors.
    /*!
          \param ax x coordinate value of the vector 0.
          \param ay y coordinate value of the vector 0.
          \param bx x coordinate value of the vector 1.
          \param by y coordinate value of the vector 1.
          \return return the angel.
    */
    static double crossAngle(const double ax, const double ay, const double bx, const double by);

    //! Determine if a point in a frustum.
    /*!
          \param p the point position.
          \param point the 8 points of the frustum which define the frustum
          \param view the view direction of the camera.
          \param up the up direction of the camera.
          \return return true if the point in the frustum, or return false..
    */
    static bool isVectorInFrustum(const Vector3 p, const Vector3 point[], const Vector3 view, const Vector3 up);

    //! Normalize a vector
    /*!
     * \brief normalize normalizes a vector
     * \param x x coordinate value of vector
     * \param y y coordinate value of vector
     * \param z z coordinate value of vector
     * \param nx x coordinate value of the normalized vector
     * \param ny y coordinate value of the normalized vector
     * \param nz z coordinate value of the normalized vector
     */
    static void normalize(const double x, const double y, const double z, double &nx, double &ny, double &nz);

    //! Compute the cross product of two 3D vectos.
    /*!
          \param x0 x coordinate value of vector 0.
          \param y0 y coordinate value of vector 0.
          \param z0 z coordinate value of vector 0.
          \param x1 x coordinate value of vector 1.
          \param y1 y coordinate value of vector 1.
          \param z1 z coordinate value of vector 1.
          \param cpx x coordinate value of the cross product
          \param cpy y coordinate value of the cross product
          \param cpz z coordinate value of the cross product
    */
    static void crossProduct(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1,
                             double &cpx, double &cpy, double &cpz);

    //! Set all the members of the matrix to be 0.
    /*!
          \param mat 4*4 matrix.
    */
    static void resetMat(double mat[4][4]);

    //! Set the members to match the identity matrix
    /*!
          \param mat 4*4 matrix.
     */
    static void identityMat(double mat[4][4]);
};

#endif // CELLMATH_H
